/*
 * Created on Mar 13, 2005
 * Created by dfhuynh
 */
package edu.mit.simile.longwell;

import java.util.Iterator;
import java.util.Set;

/**
 * @author dfhuynh
 */
public class CascadedCache {
	final protected int			m_levels;
	final protected Cache		m_rootCache;
	final protected int[]		m_sizes;
	final protected String[]	m_names;
	final protected Class		m_clientClass;
	
	public CascadedCache(
			Class clientClass,
			String firstLevelName, int firstLevelSize, 
			String secondLevelName, int secondLevelSize) {
		m_levels = 2;
		
		m_sizes = new int[m_levels];
		m_sizes[0] = firstLevelSize;
		m_sizes[1] = secondLevelSize;
		
		m_names = new String[m_levels];
		m_names[0] = firstLevelName;
		m_names[1] = secondLevelName;
		
		m_rootCache = new Cache(firstLevelSize, clientClass, firstLevelName);
		
		m_clientClass = clientClass;
	}
	
	public CascadedCache(
			Class clientClass,
			String firstLevelName, int firstLevelSize, 
			String secondLevelName, int secondLevelSize,
			String thirdLevelName, int thirdLevelSize) {
		m_levels = 3;
		
		m_sizes = new int[m_levels];
		m_sizes[0] = firstLevelSize;
		m_sizes[1] = secondLevelSize;
		m_sizes[2] = thirdLevelSize;
		
		m_names = new String[m_levels];
		m_names[0] = firstLevelName;
		m_names[1] = secondLevelName;
		m_names[2] = thirdLevelName;
		
		m_rootCache = new Cache(firstLevelSize, clientClass, firstLevelName);
		
		m_clientClass = clientClass;
	}
	
	public Object get(Object firstLevelKey, Object secondLevelKey) {
		Cache secondLevelCache = (Cache) m_rootCache.get(firstLevelKey);
		if (secondLevelCache == null) {
			secondLevelCache = new Cache(m_sizes[1], m_clientClass, m_names[1]);
			m_rootCache.put(firstLevelKey, secondLevelCache);
		}
		
		return secondLevelCache.get(secondLevelKey);
	}
	
	public Object get(Object firstLevelKey, Object secondLevelKey, Object thirdLevelKey) {
		Cache secondLevelCache = (Cache) m_rootCache.get(firstLevelKey);
		if (secondLevelCache == null) {
			secondLevelCache = new Cache(m_sizes[1], m_clientClass, m_names[1]);
			m_rootCache.put(firstLevelKey, secondLevelCache);
		}
		
		Cache thirdLevelCache = (Cache) secondLevelCache.get(secondLevelKey);
		if (thirdLevelCache == null) {
			thirdLevelCache = new Cache(m_sizes[2], m_clientClass, m_names[2]);
			secondLevelCache.put(secondLevelKey, thirdLevelCache);
		}
		
		return thirdLevelCache.get(thirdLevelKey);
	}
	
	public void put(Object firstLevelKey, Object secondLevelKey, Object value) {
		Cache secondLevelCache = (Cache) m_rootCache.get(firstLevelKey);
		if (secondLevelCache == null) {
			secondLevelCache = new Cache(m_sizes[1], m_clientClass, m_names[1]);
			m_rootCache.put(firstLevelKey, secondLevelCache);
		}
		
		secondLevelCache.put(secondLevelKey, value);
	}
	
	public void put (Object firstLevelKey, Object secondLevelKey, Object thirdLevelKey, Object value) {
		Cache secondLevelCache = (Cache) m_rootCache.get(firstLevelKey);
		if (secondLevelCache == null) {
			secondLevelCache = new Cache(m_sizes[1], m_clientClass, m_names[1]);
			m_rootCache.put(firstLevelKey, secondLevelCache);
		}
		
		Cache thirdLevelCache = (Cache) secondLevelCache.get(secondLevelKey);
		if (thirdLevelCache == null) {
			thirdLevelCache = new Cache(m_sizes[2], m_clientClass, m_names[2]);
			secondLevelCache.put(secondLevelKey, thirdLevelCache);
		}
		
		thirdLevelCache.put(thirdLevelKey, value);
	}
	
	public void removeFirstLevel(Object firstLevelKey) {
		Cache secondLevelCache = (Cache) m_rootCache.remove(firstLevelKey);
		if (secondLevelCache != null) {
			if (m_levels > 2) {
				Iterator i = secondLevelCache.keySet().iterator();
				while (i.hasNext()) {
					Cache thirdLevelCache = (Cache) i.next();
					thirdLevelCache.clear();
				}
			}
			secondLevelCache.clear();
		}
	}
	
	public void removeSecondLevel(Object firstLevelKey, Object secondLevelKey) {
		if (firstLevelKey == null) {
			Iterator i = m_rootCache.values().iterator();
			while (i.hasNext()) {
				Cache secondLevelCache = (Cache) i.next();
				
				internalRemove(secondLevelCache, secondLevelKey);
			}
		} else {
			Cache secondLevelCache = (Cache) m_rootCache.get(firstLevelKey);
			internalRemove(secondLevelCache, secondLevelKey);
		}
	}
	
	public void collectFirstLevelValues(Set values) {
		values.addAll(m_rootCache.values());
	}
	
	public void collectSecondLevelValues(Set values) {
		Iterator i = m_rootCache.values().iterator();
		while (i.hasNext()) {
			Cache secondLevelCache = (Cache) i.next();
			if (secondLevelCache != null) {
				values.addAll(secondLevelCache.values());
			}
		}
	}
	
	public void collectThirdLevelValues(Set values) {
		Iterator i = m_rootCache.values().iterator();
		while (i.hasNext()) {
			Cache secondLevelCache = (Cache) i.next();
			if (secondLevelCache != null) {
				Iterator j = secondLevelCache.values().iterator();
				while (j.hasNext()) {
					Cache thirdLevelCache = (Cache) j.next();
					if (thirdLevelCache != null) {
						values.addAll(thirdLevelCache.values());
					}
				}
			}
		}
	}
	
	protected void internalRemove(Cache secondLevelCache, Object secondLevelKey) {
		if (secondLevelCache != null) {
			Object o = secondLevelCache.remove(secondLevelKey);
			if (m_levels > 2) {
				Cache thirdLevelCache = (Cache) o;
				thirdLevelCache.clear();
			}
		}
	}
}
